<?php
/**
 * FileSystemManager
 *
 * @category	FileSystem
 * @package		dayscript.filesystem
 * @subpackage
 * @author		Noel Marin <nmarin@dayscript.com>
 * @copyright	2009 Dayscript Ltda.
 * @license
 * @version		1.0
 * @version		$Revision: 0 $
 * @filesource
 * @link		http://www.dayscript.com
 * @link		{docsLink}
 * @uses
 * @since		1.0
 * @modifiedby	$LastChangedBy: Noel Marin $
 * @modified	$Date: 2009-08-28 $
 */
namespace dayscript\filesystem;

/**
 * FileSystemManager
 *
 * Manage all FileSystem object actions
 *
 * @category	FileSystem
 * @package		dayscript.filesystem
 * @subpackage
 * @author		Noel Marin <nmarin@dayscript.com>
 * @copyright	2009 Dayscript Ltda.
 * @license
 * @version		1.0
 * @link		http://www.dayscript.com
 * @link		{docsLink}
 * @uses		\dayscript\exception\ErrorCallerException
 * @since		1.0
 * @modifiedby	$LastChangedBy: Noel Marin $
 * @modified	$Date: 2009-08-28 $
 */
class FileSystemManager {

	/**
	 * Default constructor
	 *
	 * @return void
	 */
	protected function __construct(){}

	/**
	 * Opens a FileSystem in a specified mode
	 *
	 * @param $fileSystem
	 * @return $fileSystem FileSystem within pointer resource
	 */
	public static function open(\dayscript\filesystem\FileSystem $fileSystem){
		if(!isset($fileSystem)){
			throw new \dayscript\exception\ErrorCallerException( "File is null ", E_USER_ERROR );
		}

		$mode = $fileSystem->getMode();
		$exist = $fileSystem->isExist();
		if(!$exist and ($mode == FileSystem::MODE_ONLY_READ_B_NC or $mode == FileSystem::MODE_READ_WRITE_B_NC)){
			throw new \dayscript\exception\ErrorCallerException( "File does not exists and not attempt to create it", E_USER_WARNING );
		}

		if($fileSystem->isDirectory()){
			throw new \dayscript\exception\ErrorCallerException( "To open directories use DirectorySystemManager::open ", E_USER_WARNING );
		}

		if($mode != FileSystem::MODE_ONLY_WRITE_B_C and $mode != FileSystem::MODE_ONLY_WRITE_E_C and $mode != FileSystem::MODE_ONLY_WRITE_B_C_F  and $exist and !$fileSystem->isReadable()){
			throw new \dayscript\exception\ErrorCallerException( "File is not readable ", E_USER_WARNING );
		}

		if($mode != FileSystem::MODE_ONLY_READ_B_NC and $exist and !$fileSystem->isWriteable()){
			throw new \dayscript\exception\ErrorCallerException( "File is not writeable ", E_USER_WARNING );
		}

		// A file will always open with 'b' flag for portability
		$handle = @\fopen($fileSystem->getName(), $mode . FileSystem::MODE_PORTABILITY); //TO-DO see if it's necesary use $context

		if($handle === false){
			throw new \dayscript\exception\ErrorException( "File cannot be opened, see file permissions ", E_USER_WARNING, 0, __FILE__, __LINE__ );
		}

		if($mode != FileSystem::MODE_ONLY_READ_B_NC and $mode != FileSystem::MODE_READ_WRITE_B_NC){
			$fileSystem->setReadable();
			$fileSystem->setWriteable();
		}

		$fileSystem->setHandle($handle);
	}

	/**
	 * Reads an opended FileSystem by length
	 *
	 * @param $fileSystem The FileSystem to be readed
	 * @param $length Number of bytes to be readed, if 0 read the entire FileSystem
	 * @return string
	 */
	public static function readByLength(\dayscript\filesystem\FileSystem $fileSystem, $length = 0){
		$handle = $fileSystem->getHandle();
		if(!isset($handle)){
			throw new \dayscript\exception\ErrorCallerException( "Open it first ", E_USER_WARNING );
		}

		if(!$fileSystem->isReadable()){
			throw new \dayscript\exception\ErrorCallerException( "Only write mode ", E_USER_WARNING );
		}

		$size = $fileSystem->getSize();
		if($size == 0){
			throw new \dayscript\exception\ErrorCallerException( "Empty file ", E_USER_NOTICE );
		}

		$content = "";

		if($length == 0){
			$content = \fread($handle, $size);
		}else{
			$content = \fread($handle, $length);
		}
		$fileSystem->setHandle($handle);

		return $content;
	}

	/**
	 * Reads an opended FileSystem by line
	 *
	 * @param $fileSystem The FileSystem to be readed
	 * @param $length Number of bytes to be readed, if 0 read the entire FileSystem
	 * @return string
	 */
	public static function readByLine(\dayscript\filesystem\FileSystem $fileSystem){
		$handle = $fileSystem->getHandle();
		if(!isset($handle)){
			throw new \dayscript\exception\ErrorCallerException( "Open it first ", E_USER_WARNING );
		}

		if(!$fileSystem->isReadable()){
			throw new \dayscript\exception\ErrorCallerException( "Only write mode ", E_USER_WARNING );
		}

		if($fileSystem->getSize() == 0){
			throw new \dayscript\exception\ErrorCallerException( "Empty file ", E_USER_NOTICE );
		}

		$content = \fgets($handle);

		$fileSystem->setHandle($handle);

		return $content;
	}

	/**
	 * Writes the contents of $content to the file
	 *
	 * @param $fileSystem The FileSystem
	 * @param $content The string that is to be written
	 * @param $length If It's given, writing will stop after length bytes have been written or the end of string is reached, whichever comes first
	 * @return void
	 */
	public static function write(\dayscript\filesystem\FileSystem $fileSystem, $content, $length = 0){
		$handle = $fileSystem->getHandle();
		if(!isset($handle)){
			throw new \dayscript\exception\ErrorCallerException( "Open it first ", E_USER_WARNING );
		}

		if(!$fileSystem->isWriteable()){
			throw new \dayscript\exception\ErrorCallerException( "Only read mode ", E_USER_WARNING );
		}

		$length == 0 ? \fwrite($handle, $content) : \fwrite($handle, $content, $length);

		$fileSystem->setHandle($handle);
	}

	/**
	 * Rewinds the position of a file pointer
	 *
	 * @param $fileSystem
	 * @return void
	 */
	public static function rewind(\dayscript\filesystem\FileSystem $fileSystem){
		$handle = $fileSystem->getHandle();
		if(!isset($handle)){
			throw new \dayscript\exception\ErrorCallerException( "Open it first ", E_USER_WARNING );
		}

		\rewind($handle);

		$fileSystem->setHandle($handle);
	}

	/**
	 * Closes an FileSystem
	 *
	 * @param $fileSystem
	 * @return $fileSystem
	 */
	public static function close(\dayscript\filesystem\FileSystem $fileSystem){
		$handle = $fileSystem->getHandle();
		if(!isset($handle)){
			throw new \dayscript\exception\ErrorCallerException( "Open it first ", E_USER_WARNING );
		}

		\fclose($handle);
		$fileSystem->setHandle(null);
	}

	/**
	 * Deletes a FileSystem
	 *
	 * @param $fileSystem
	 * @return boolean
	 */
	public static function delete(\dayscript\filesystem\FileSystem $fileSystem){
		if($fileSystem->isDirectory()){
			throw new \dayscript\exception\ErrorCallerException( "Parameter is directory, to delete it use DirectorySystemManager::delete() ", E_USER_WARNING );
		}

		if(!$fileSystem->isWriteable()){
			throw new \dayscript\exception\ErrorCallerException( "Must be writeable to delete ", E_USER_WARNING );
		}

		$parent = new DirectorySystem($fileSystem->getDirectoryName());
		if(!$parent->isWriteable()){
			throw new \dayscript\exception\ErrorCallerException( "Parent directory must be writeable too ", E_USER_WARNING );
		}

		return \unlink($fileSystem->getName());
	}

	/**
	 * Creates complete FileSystem path
	 *
	 * @param $fileSystem
	 * @param $lastIsFile Tells whether the last path's part is a file or not
	 * @return boolean Success or not
	 */
	public static function createCompletePath(\dayscript\filesystem\FileSystem $fileSystem, $lastIsFile, $mode = 0777){
		if($fileSystem->exists()){
			throw new \dayscript\exception\ErrorCallerException( "File already exists ", E_USER_NOTICE );
		}

		$path = $fileSystem->getExplodeName();
		$n = \count($path);

		if($n == 0){
			throw new \dayscript\exception\ErrorCallerException( "Wrong path ", E_USER_ERROR );
		}

		for($i = 0; $i < $n ; $i++){
				

			$completeName = "";
			for($j = 0; $j < $i ; $j++){
				$completeName .= $path[$j] . "/";
			}

			$tmp = new \dayscript\filesystem\DirectorySystem($completeName . $path[$i], FileSystem::MODE_ONLY_WRITE_B_C);

			if($lastIsFile){
				if($i < $n - 1){
					if(!$tmp->exists()){
						$tmp->setMode($mode);
						\dayscript\filesystem\DirectorySystemManager::create($tmp);
					}
				}else{
					self::open($tmp);
					self::close($tmp);
				}
			}else{
				if(!$tmp->exists()){
					$tmp->setMode($mode);
					\dayscript\filesystem\DirectorySystemManager::create($tmp);
				}
			}
		}

		return $fileSystem->exists();
	}

}